package com.mp.approvalreq.util;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.mp.approvalreq.dao.AppendSignDao;
import com.mp.approvalreq.dao.SelectTemplateDao;
import com.mp.approvalreq.dao.SubmitRequestDao;
import com.mp.approvalreq.entity.*;
import com.mp.approvalreq.entity.po.ComponentGroup;
import com.mp.approvalreq.entity.vo.Component;
import com.mp.checkin.service.CheckInDataServicePrx;
import com.mp.user.entity.User;
import com.plusmoney.exception.RpcServerLogicalException;
import com.plusmoney.util.DateTimeHelper;
import com.plusmoney.util.Utils;
import com.qq.tars.client.util.ServantUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 组件检查
 */
public class ComponentCheck {

    private static final Logger logger = LoggerFactory.getLogger(ComponentCheck.class);

    private static void checkWorkOverTimeTemplate(List<TemplateComponent> templateComponents, List<TemplateComponent> componentsContent, int generation, int defaultType, User user) throws Throwable {
        if (defaultType == Template.WORK_OVERTIME) {
            if (generation < 3) {
                throw new RpcServerLogicalException(1, "请更新大管加版本");
            }

            SimpleDateFormat sdf = DateUtils.getSingleSDF(DateUtils.YMDHM);
            Long startTime = null;
            Long endTime = null;

            for (TemplateComponent templateComponent : templateComponents) {
                for (TemplateComponent componentContent : componentsContent) {
                    if (!Objects.equals(templateComponent.getNum(), componentContent.getNum())) {
                        continue;
                    }

                    int need = templateComponent.getNeed();
                    int num = templateComponent.getNum();
                    String name = templateComponent.getName();
                    String value = componentContent.getValue();

                    if (need == 1) {
                        if (StringUtils.isEmpty(value)) {
                            throw new RpcServerLogicalException(1, name + "不能为空");
                        }
                    }

                    if (num == 1 || num == 2) {
                        if (!ValidateUtil.isValidDateTime(value)) {
                            throw new RpcServerLogicalException(1, name + "格式不正确");
                        }

                        if (num == 1) {
                            startTime = parseTime(value, sdf);
                        }

                        if (num == 2) {
                            endTime = parseTime(value, sdf);
                        }
                    }

                    // 前端硬算的加班时长
                    if (num == 3) {
                        if (StringUtils.isEmpty(value)) {
                            throw new RpcServerLogicalException(1, name + "不能为空");
                        }
                    }
                }
            }
            checkTime(startTime, endTime);

            checkExistCheckinScheme(user, startTime);
        }
    }

    private static void checkExistCheckinScheme(User user, Long dateTimeLong) throws Throwable {
        CheckInDataServicePrx checkInDataServicePrx = ServantUtils.getObject(CheckInDataServicePrx.class);
        if (Utils.isNotNull(dateTimeLong)) {
            String date = DateTimeHelper.format(new Date(dateTimeLong));
            boolean existScheme = checkInDataServicePrx.isExistScheme(user.getCompanyId(), user.getCompanyInfoId(), date);
            if (!existScheme) {
                throw new RpcServerLogicalException(1, "该申请时间段无考勤方案");
            }
        }
    }

    private static Long parseTime(String time, SimpleDateFormat sdf) {
        try {
            return sdf.parse(time).getTime();
        } catch (ParseException e) {
            throw new RpcServerLogicalException(1, "时间格式不正确");
        }
    }


    public static void checkWorkOverTime(Template template, Request request) {
        if (template.getDefaultType() == Template.WORK_OVERTIME) {
            // 加班审批只有generation >=3 的前端版本才能使用
            if (Utils.isNotNull(request.getGeneration()) && request.getGeneration() < 3) {
                throw new RpcServerLogicalException(1, "请更新大管加版本");
            }

            // 数据库模板组件
            List<TemplateComponent> templateComponents = JSONArray.parseArray(template.getTemplateComponent(), TemplateComponent.class);
            // 组件内容
            List<TemplateComponent> componentsContent = request.getComponentsContent();
            if (ListHelper.isNullEmpty(componentsContent)) {
                throw new RpcServerLogicalException(1, "审批内容提交失败，请稍候再试");
            }

            SimpleDateFormat sdf = DateUtils.getSingleSDF(DateUtils.YMDHM);

            Long startTime = null;
            Long endTime = null;
            for (TemplateComponent templateComponent : templateComponents) {
                for (TemplateComponent componentContent : componentsContent) {
                    if (!Objects.equals(templateComponent.getNum(), componentContent.getNum())) {
                        continue;
                    }

                    int need = templateComponent.getNeed();
                    int num = templateComponent.getNum();
                    String name = templateComponent.getName();
                    String value = componentContent.getValue();

                    if (need == 1) {
                        if (StringUtils.isEmpty(value)) {
                            throw new RpcServerLogicalException(1, name + "不能为空");
                        }
                    }

                    if (num == 1 || num == 2) {
                        if (!ValidateUtil.isValidDateTime(value)) {
                            throw new RpcServerLogicalException(1, name + "格式不正确");
                        }

                        try {
                            if (num == 1) {
                                startTime = sdf.parse(value).getTime();
                            }

                            if (num == 2) {
                                endTime = sdf.parse(value).getTime();
                            }
                        } catch (Exception e) {
                            throw new RpcServerLogicalException(1, name + "格式不正确");
                        }
                    }

                    if (num == 3) {
                        if (StringUtils.isEmpty(value)) {
                            throw new RpcServerLogicalException(1, name + "不能为空");
                        }
                    }
                }
            }

            checkTime(startTime, endTime);
        }
    }

    private static void checkTime(Long startTime, Long endTime) {
        if (Utils.isNull(startTime) || Utils.isNull(endTime)) {
            throw new RpcServerLogicalException(1, "请选择开始时间或结束时间");
        }

        if (startTime > endTime) {
            throw new RpcServerLogicalException(1, "开始时间不得晚于结束时间");
        }
    }


    private static void checkExpenseTemplate(List<TemplateComponent> templateComponents, List<TemplateComponent> componentsContent, boolean isNewTemplate, int defaultType) {
        if (defaultType == Template.EXPENSES) {
            if (isNewTemplate) {
                newExpenseCheck(templateComponents, componentsContent);
            } else {
                oldExpenseCheck(templateComponents, componentsContent);
            }
        }
    }

    /**
     * 报销模板的检测
     *
     * @param req
     * @param template
     * @throws Throwable
     */
    public static void checkExpenseTemplate(Request req, Template template) {
        if (template.getDefaultType() == Template.EXPENSES) {
            List<TemplateComponent> databaseTcList = JSONArray.parseArray(template.getTemplateComponent(), TemplateComponent.class);
            List<TemplateComponent> tcList = req.getComponentsContent();
            if (ListHelper.isNullEmpty(tcList)) {
                throw new RpcServerLogicalException(1, "模板组件为空");
            }

            // 判断是否是新的报销模板,totalMoney不为空
            boolean isNewTemplate = req.getTotalMoney() != null;

            if (isNewTemplate) {
                newExpenseCheck(databaseTcList, tcList);
            } else {
                oldExpenseCheck(databaseTcList, tcList);
            }
        }
    }

    /**
     * 老版本报销模板检测
     */
    private static void oldExpenseCheck(List<TemplateComponent> databaseTcList, List<TemplateComponent> tcList) {
        for (TemplateComponent dataTc : databaseTcList) {   // 数据库模板组件
            for (TemplateComponent valueTc : tcList) {       // 前端模板组件
                if (!Objects.equals(dataTc.getNum(), valueTc.getNum())) {
                    continue;
                }

                if (dataTc.getNeed() == 1) {
                    if (StringUtils.isEmpty(valueTc.getValue())) {
                        throw new RpcServerLogicalException(1, "请填写必填项:" + dataTc.getName());
                    }
                }

                if (dataTc.getNum() == 1) {                                                          // 报销事项
                    checkReimbursementItem(valueTc.getValue(), valueTc.getName(), false);
                }

                if (dataTc.getNum() == 2 && dataTc.getType() == ContentType.MONEY) {                    // 报销金额
                    checkReimbursementMoney(valueTc.getValue(), valueTc.getName(), false);
                }

                if (dataTc.getNum() == 3 && dataTc.getType() == ContentType.DATE) {                    // 支出日期
                    checkExpendDate(valueTc.getValue(), valueTc.getName(), false);
                }
            }
        }
    }


    /**
     * fixme: 由于不可抗力的因素,默认的报销模板进行了较大改动
     * 组件报销事项、报销金额、支出日期这三个组件的数据格式是json数组: ["xx","xx"]
     * 数组的数量表示的是,该条审批共有多少个报销事项,具体可以联系页面图参考.
     */
    private static void newExpenseCheck(List<TemplateComponent> templateComponents, List<TemplateComponent> componentsContent) {
        int reimbursementItemSize = 0;
        int reimbursementMoneySize = 0;
        int expendDateSize = 0;

        for (TemplateComponent templateComponent : templateComponents) {   // 数据库模板组件
            int need = templateComponent.getNeed();
            String name = templateComponent.getName();
            boolean pass = false;

            for (TemplateComponent componentContent : componentsContent) {        // 前端模板组件
                if (!Objects.equals(templateComponent.getNum(), componentContent.getNum())) {
                    continue;
                }

                int num = templateComponent.getNum();
                int type = templateComponent.getType();
                String value = componentContent.getValue();
                pass = true;

                if (need == 1) {
                    if (StringUtils.isEmpty(value)) {
                        throw new RpcServerLogicalException(1, "请填写必填项:" + name);
                    }

                    if (num == 1) {                                               // 报销事项
                        checkReimbursementItem(value, name, true);
                        logger.info("报销事项:{}", value);
                        reimbursementItemSize = JSONArray.parseArray(value).size();
                    }

                    if (num == 2 && type == ContentType.MONEY) {                 // 报销金额
                        checkReimbursementMoney(value, name, true);
                        logger.info("报销金额:{}", value);
                        reimbursementMoneySize = JSONArray.parseArray(value).size();
                    }

                    if (num == 3 && type == ContentType.DATE) {                   // 支出日期
                        checkExpendDate(value, name, true);
                        logger.info("支出日期:{}", value);
                        expendDateSize = JSONArray.parseArray(value).size();
                    }
                }
            }
            if (need == 1 && !pass) {
                throw new RpcServerLogicalException(1, "请填写必填项:" + name);
            }
        }

        if (reimbursementItemSize != reimbursementMoneySize || reimbursementItemSize != expendDateSize) {

            logger.info("报销事项:{}, 报销金额:{}, 支出日期:{}", reimbursementItemSize, reimbursementMoneySize, expendDateSize);
            throw new RpcServerLogicalException(1, "客戶端参数格式不正确");
        }
    }


    /**
     * 报销事项的检测
     * 新版报销,报销事项数据格式: json数组
     * 原版报销,报销事项为普通字符串
     */
    private static void checkReimbursementItem(String tcValue, String tcName, boolean isNewExpenseVersion) {
        if (isNewExpenseVersion) {
            JSONArray array = JSONArray.parseArray(tcValue);
            if (array.isEmpty()) {
                throw new RpcServerLogicalException(1, tcName + " 输入不能为空");
            }
            for (int i = 0; i < array.size(); i++) {
                String item = array.getString(i);
                if (StringUtils.isEmpty(item)) {
                    throw new RpcServerLogicalException(1, tcName + " 输入不能为空");
                }
            }
        }
        // 原版的判空校验,放外面
    }


    /**
     * 报销金额的校验
     * 新版报销,报销金额数据格式: json数组
     * 原版报销,报销金额为普通字符串
     */
    private static void checkReimbursementMoney(String tcValue, String tcName, boolean isNewExpenseVersion) {
        if (isNewExpenseVersion) {
            JSONArray arr = JSONArray.parseArray(tcValue);
            if (arr.isEmpty()) {
                throw new RpcServerLogicalException(1, tcName + " 输入不能为空");
            }
            for (int i = 0; i < arr.size(); i++) {
                String item = arr.getString(i);
                if (!ValidateUtil.isMoney(item)) {
                    throw new RpcServerLogicalException(1, tcName + " 仅支持输入数字");
                }
            }

        } else {

            if (!ValidateUtil.isMoney(tcValue)) {
                throw new RpcServerLogicalException(1, tcName + "  仅支持输入数字");
            }
        }
    }

    /**
     * 支出日期的检测 : yyyy-mm-dd
     * 新版报销,支出日期数据格式: json数组
     * 原版报销,支出日期为普通字符串
     */
    private static void checkExpendDate(String tcValue, String tcName, boolean isNewExpenseVersion) {
        if (isNewExpenseVersion) {
            JSONArray arr = JSONArray.parseArray(tcValue);
            if (arr.isEmpty()) {
                throw new RpcServerLogicalException(1, tcName + " 输入不能为空");
            }
            for (int i = 0; i < arr.size(); i++) {
                String value = arr.getString(i);
                if (!ValidateUtil.isValidDate(value)) {
                    throw new RpcServerLogicalException(1, tcName + "格式不正确");
                }
            }
        } else {
            if (!ValidateUtil.isValidDate(tcValue)) {
                throw new RpcServerLogicalException(1, tcName + "格式不正确");
            }
        }
    }


    /**
     * 补打卡模板的检查
     *
     * @param request
     * @param template
     * @param appendSignDao
     */
    public static void checkCheckInTemplate(Request request, Template template, AppendSignDao appendSignDao, User user) throws Throwable {
        if (template.getDefaultType() == Template.APPEND_SIGN) {
            if (Utils.isNotNull(request.getGeneration()) && request.getGeneration() < 2) {
                throw new RpcServerLogicalException(1, "请更新大管加版本");
            }
            fillClockApplyLimit(request, template, appendSignDao, user);

            newCheckInCheck(request, template, user, appendSignDao);
        }
    }


    private static void fillClockApplyLimit(Request request, Template template, AppendSignDao appendSignDao, User user) throws Throwable {
        if (Utils.isNull(template.getIsMaxTime()) || template.getIsMaxTime() == 0) {
            return;
        }
        Map<Integer, Integer> timesMap = appendSignDao.selectUserAppendSignsTimesMapByMonth(user.getCompanyId(), user.getCompanyInfoId(), DateUtils.getCurrentMonth());
        Integer times = timesMap.get(template.getAncestorId());
        // 已消耗次数
        int totalCount = Utils.isNull(times) ? 0 : times;
        // 本次消耗次数
        int currentTimes = ComponentUtils.countByContentType(request.getComponentsContent(), ContentType.TIME);
        logger.info("已消耗:{},本次消耗:{},最大次数:{}", totalCount, currentTimes, template.getMaxTimePermonth());
        if (totalCount + currentTimes > template.getMaxTimePermonth()) {
            throw new RpcServerLogicalException(1, "本月申请次数已达上限");
        }

    }


    /**
     * 申请次数限制
     */
    private static void applyNumberLimit(int maxTimePerMonth, SubmitRequestDao submitRequestDao, int companyInfoId, int defaultType) throws Throwable {
        if (maxTimePerMonth != 0) {
            int times = submitRequestDao.getTimesCurrentMonthPersonUsed(companyInfoId, defaultType);

            if (times >= maxTimePerMonth) {
                throw new RpcServerLogicalException(1, "本月申请次数已达上限");
            }
        }
    }

    /**
     * 申请内容时间限制，天为单位，N天内申请有效
     *
     * @param checkInTime      : 补卡时间: y-m-d
     * @param contentTimeLimit : 限制天数
     *                         例如: 补卡时间,2019-01-03, 2天内申请--> 则在2019-01-05 23:59:59内都可以申请
     */
    private static void applyTimeLimit(int contentTimeLimit, long checkInTime, int templateType, long nowTimes) {
        if (contentTimeLimit != 0) {

            // 不能用int,溢出会出负数
            long limitDayLongTime = 1000 * 60 * 60 * 24 * ((long) contentTimeLimit + 1);

            if ((nowTimes - checkInTime) > limitDayLongTime) {
                if (templateType == Template.APPEND_SIGN) {
                    throw new RpcServerLogicalException(1, "请于忘打卡后的" + (long) contentTimeLimit + "天内申请补卡");
                } else if (templateType == Template.COMPENSATORY_LEAVE) {
                    throw new RpcServerLogicalException(1, "请于忘请假后的" + (long) contentTimeLimit + "天内申请补假");
                }
            }
        }
    }


    private static void newCheckInCheck(Request request, Template template, User user, AppendSignDao appendSignDao) throws Throwable {

        List<TemplateComponent> templateComponents = JSONArray.parseArray(template.getTemplateComponent(), TemplateComponent.class);
        List<TemplateComponent> tcList = request.getComponentsContent();
        if (ListHelper.isNullEmpty(tcList)) {
            throw new RpcServerLogicalException(1, "模板组件为空");
        }


        for (TemplateComponent component : templateComponents) {
            for (TemplateComponent content : tcList) {
                if (!Objects.equals(content.getNum(), component.getNum())) {
                    continue;
                }

                // 缺卡时间/补卡类型/缺卡原因
                if (component.getNum() == 1 || component.getNum() == 2 || component.getNum() == 10) {
                    if (StringUtils.isEmpty(content.getValue())) {
                        throw new RpcServerLogicalException(1, component.getName() + " 不能为空");
                    }
                }

                // 新版补打卡: 1和6都是时间组件
                if (component.getNum() == 1 || component.getNum() == 6) {
                    // 只申请一条补打卡时,num=6,前端不传数据
                    if (StringUtils.isEmpty(content.getValue())) {
                        continue;
                    }

                    SimpleDateFormat sdf = DateUtils.getSingleSDF(DateUtils.YMDHM);
                    SimpleDateFormat dateFormat = DateUtils.getSimpleDateFormat(DateUtils.YMD);
                    long checkInTime;
                    try {
                        checkInTime = sdf.parse(content.getValue()).getTime();
                    } catch (ParseException e) {
                        throw new RpcServerLogicalException(1, component.getName() + " 格式错误");
                    }

                    if (checkInTime > System.currentTimeMillis()) {
                        throw new RpcServerLogicalException(1, "申请时间须早于当前时间");
                    }
                    long nowTimes = System.currentTimeMillis();
                    try {
                        if (Utils.isNotNull(request.getParentRequestId()) && request.getParentRequestId() > 0) {
                            Date date = appendSignDao.selectCreateTimeByRequestId(request.getParentRequestId());
                            if (Utils.isNotNull(date)) {
                                nowTimes = date.getTime();
                                logger.info("调整后的补卡申请时间:{}", date);
                            }
                        }
                    } catch (Throwable e) {
                        EmailUtils.sendMail("调整补卡时间出错", e);
                    }
                    // 申请时间限制
                    Date signTime = dateFormat.parse(content.getValue());
                    try {
                        applyTimeLimit(template.getContentTimeLimit(), signTime.getTime(), Template.APPEND_SIGN, nowTimes);
                    } catch (RpcServerLogicalException e) {
                        logger.error(e.getMessage(), e);
                        CheckInDataServicePrx checkInDataServicePrx = ServantUtils.getObject(CheckInDataServicePrx.class);
                        String date = checkInDataServicePrx.getWorkDayOffset(user.getCompanyId(), user.getCompanyInfoId(), DateTimeHelper.format(signTime), template.getContentTimeLimit());
                        if (DateTimeHelper.format(new Date(nowTimes)).compareTo(date) > 0) {
                            throw e;
                        }
                    }
                    // 判断是否存在考勤方案
                    checkExistCheckinScheme(user, checkInTime);
                }
            }
        }

    }


    /**
     * 补假模板的检测
     */
    public static void checkCompensatoryLaveTemplate(Request request, Template template, SubmitRequestDao submitRequestDao, User user) throws Throwable {
        if (template.getDefaultType() == Template.COMPENSATORY_LEAVE) {
            List<TemplateComponent> templateComponents = JSONArray.parseArray(template.getTemplateComponent(), TemplateComponent.class);
            List<TemplateComponent> componentsContent = request.getComponentsContent();
            if (ListHelper.isNullEmpty(componentsContent)) {
                throw new RpcServerLogicalException(1, "模板组件为空");
            }

            checkLeaveThreeParam(request, Template.COMPENSATORY_LEAVE);

            SimpleDateFormat sdf = DateUtils.getSingleSDF(DateUtils.YMDHM);
            SimpleDateFormat dateFormat = DateUtils.getSimpleDateFormat(DateUtils.YMD);

            long startTime = 0L;
            long endTime = 0L;
            long startDate = 0L;

            for (TemplateComponent templateComponent : templateComponents) {
                for (TemplateComponent componentContent : componentsContent) {
                    if (!Objects.equals(templateComponent.getNum(), componentContent.getNum())) {
                        continue;
                    }

                    int num = templateComponent.getNum();
                    int need = templateComponent.getNeed();
                    String name = templateComponent.getName();
                    String value = componentContent.getValue();

                    if (need == 1) {
                        if (StringUtils.isEmpty(value)) {
                            throw new RpcServerLogicalException(1, name + " 不能为空");
                        }
                    }

                    if (num == 1 || num == 2) {
                        if (!ValidateUtil.isValidDateTime(value)) {
                            throw new RpcServerLogicalException(1, name + "格式不正确");
                        }

                        if (num == 1) {
                            startTime = parseTime(value, sdf);
                            startDate = parseTime(value, dateFormat);
                        }

                        if (num == 2) {
                            endTime = parseTime(value, sdf);
                        }
                    }
                }
            }

            if (startTime > endTime) {
                throw new RpcServerLogicalException(1, "结束时间须晚于开始时间");
            }

            if (startTime > System.currentTimeMillis()) {
                throw new RpcServerLogicalException(1, "补假开始时间不得晚于当前时间");
            }
            if (DateUtils.countDays(endTime, startTime) > DateUtils.LEAVE_LIMITED) {
                throw new RpcServerLogicalException(1, "补假时长不能超过200天");
            }
            long nowTimes = System.currentTimeMillis();

            // 开始时间的限制
            applyTimeLimit(template.getContentTimeLimit(), startDate, Template.COMPENSATORY_LEAVE, nowTimes);

            // 申请次数限制
            applyNumberLimit(template.getMaxTimePermonth(), submitRequestDao, user.getCompanyInfoId(), template.getDefaultType());
        }
    }


    /**
     * 新版请假模板校验
     *
     * @param request
     */
    private static void checkLeaveTemplateByGeneration(Request request, List<TemplateComponent> templateComponents, String timeZone, int defaultType) {
        if (defaultType == Template.LEAVE) {
            checkLeaveTemplate(request, templateComponents, timeZone, defaultType);
        }
    }


    /**
     * 检测请假模板
     *
     * @param request
     */
    private static void checkLeaveTemplate(Request request, List<TemplateComponent> templateComponents, String timeZone, int defaultType) {
        checkLeaveThreeParam(request, defaultType);

        SimpleDateFormat sdf = DateUtils.getSimpleDateFormat(DateUtils.YMDHM);
        long startTime = 0;
        long endTime = 0;

        List<TemplateComponent> componentsContent = request.getComponentsContent();
        for (TemplateComponent templateComponent : templateComponents) {
            for (TemplateComponent componentContent : componentsContent) {
                if (!Objects.equals(componentContent.getNum(), templateComponent.getNum())) {
                    continue;
                }

                int num = templateComponent.getNum();
                int need = templateComponent.getNeed();
                String name = templateComponent.getName();
                String value = componentContent.getValue();

                if (need == 1) {
                    if (StringUtils.isEmpty(value)) {
                        throw new RpcServerLogicalException(1, name + " 不能为空");
                    }
                }

                if (num == 1 || num == 2) {
                    if (!ValidateUtil.isValidDateTime(value)) {
                        throw new RpcServerLogicalException(1, name + "格式不正确");
                    }

                    if (num == 1) {
                        startTime = parseTime(value, sdf);
                    }

                    if (num == 2) {
                        endTime = parseTime(value, sdf);
                    }
                }
            }
        }

        checkLeaveTime(startTime, endTime);

        if (StringUtils.isNotEmpty(timeZone) && timeZone.contains("Asia/Shanghai")) {
            if (startTime < (System.currentTimeMillis())) {
                throw new RpcServerLogicalException(1, "请假开始时间须晚于当前时间");
            }
        }
    }


    private static void checkLeaveTime(long startTime, long endTime) {
        if (startTime == 0 || endTime == 0) {
            throw new RpcServerLogicalException(1, "请选择时间");
        }

        if ((startTime - endTime) > 0) {
            throw new RpcServerLogicalException(1, "结束时间须晚于开始时间");
        }

        // 最大请假时间不能超过半年
        if (DateUtils.countDays(endTime, startTime) > DateUtils.LEAVE_LIMITED) {
            throw new RpcServerLogicalException(1, "已超出最大请假时间");
        }
    }


    private static void checkOutDoorTemplate(List<TemplateComponent> templateComponents, List<TemplateComponent> componentsContent, int defaultType) {
        if (defaultType == Template.OUTDOOR) {
            SimpleDateFormat sdf = DateUtils.getSingleSDF(DateUtils.YMDHM);
            long startTime = 0;
            long endTime = 0;

            for (TemplateComponent templateComponent : templateComponents) {
                for (TemplateComponent componentContent : componentsContent) {
                    if (!Objects.equals(templateComponent.getNum(), componentContent.getNum())) {
                        continue;
                    }

                    int num = templateComponent.getNum();
                    int need = templateComponent.getNeed();
                    String name = templateComponent.getName();
                    String value = componentContent.getValue();

                    if (need == 1) {
                        if (StringUtils.isEmpty(value)) {
                            throw new RpcServerLogicalException(1, name + " 不能为空");
                        }
                    }

                    if (num == 1 || num == 2) {
                        if (!ValidateUtil.isValidDateTime(value)) {
                            throw new RpcServerLogicalException(1, name + "格式不正确");
                        }

                        if (num == 1) {
                            startTime = parseTime(value, sdf);
                        }

                        if (num == 2) {
                            endTime = parseTime(value, sdf);
                        }
                    }
                }
            }


            if (startTime == 0 || endTime == 0) {
                throw new RpcServerLogicalException(1, "请选择时间");
            }

            if (startTime > endTime) {
                throw new RpcServerLogicalException(1, "返回时间须晚于外出时间");
            }

            if (TimeUtils.compareWithCurrentTimeWithoutTime(startTime) == -1) {

                throw new RpcServerLogicalException(1, "外出日期不得早于当前日期");
            }

        }
    }


    /**
     * 请假、外出的模板检测
     *
     * @param request
     * @param template
     */
    public static void checkLeaveAndOutDoorTemplate(Request request, Template template, User user) {
        int defaultType = template.getDefaultType();

        checkLeaveThreeParam(request, defaultType);

        if (defaultType == Template.LEAVE || defaultType == Template.OUTDOOR) {        // 请假或者外出的模板类型

            SimpleDateFormat sdf = DateUtils.getSingleSDF(DateUtils.YMDHM);

            long startTime = 0;
            long endTime = 0;

            List<TemplateComponent> componentsContent = request.getComponentsContent();
            for (TemplateComponent component : componentsContent) {

                // 请假、外出组件的num=1,num=2(开始时间、结束时间)
                try {
                    if (component.getNum() == 1) {
                        startTime = sdf.parse(component.getValue()).getTime();
                    }
                    if (component.getNum() == 2) {
                        endTime = sdf.parse(component.getValue()).getTime();
                    }
                } catch (Exception e) {
                    throw new RpcServerLogicalException(1, "时间格式有误");
                }
            }

            if (startTime == 0 || endTime == 0) {
                throw new RpcServerLogicalException(1, "请选择时间");
            } else {
                if ((startTime - endTime) > 0) {
                    throw new RpcServerLogicalException(1, "结束时间须晚于开始时间");
                }
            }

            if (defaultType == Template.LEAVE) {
                String timeZone = user.getTimeZone();
                if (StringUtils.isNotEmpty(timeZone) && timeZone.contains("Asia/Shanghai")) {
                    if (startTime < System.currentTimeMillis() || endTime < System.currentTimeMillis()) {
                        throw new RpcServerLogicalException(1, "请假开始时间须晚于当前时间");
                    }
                }

                // 最大请假时间不能超过一年
                if (DateUtils.countDays(endTime, startTime) > DateUtils.LEAVE_LIMITED) {
                    throw new RpcServerLogicalException(1, "请假时长不能超过200天");
                }
            }

            if (defaultType == Template.OUTDOOR) {
                if (TimeUtils.compareWithCurrentTimeWithoutTime(startTime) == -1) {
                    throw new RpcServerLogicalException(1, "外出日期不得早于当前日期");
                }
            }

            // 是否是年假、调休
            request.setIsAnnualLeave(existLeaveType(componentsContent) ? 1 : 0);
//			request.setIsDaysOff(existLeaveType(componentsContent, "调休") ? 1 : 0);
        }
    }

    private static void checkLeaveThreeParam(Request request, int defaultType) {
        if (defaultType == Template.LEAVE || defaultType == Template.COMPENSATORY_LEAVE) {
            if (StringUtils.isEmpty(request.getNaturalContentTimeLast())) {
                throw new RpcServerLogicalException(1, "时长参数错误");
            }

            if (StringUtils.isEmpty(request.getRequestContentLastTotalSecond())) {
                throw new RpcServerLogicalException(1, "时长参数错误");
            }
        }
    }

    /**
     * @param componentsContent,type 组件内容,请假补假类型
     * @auther Liquid
     * @description 判断是否是该类型。
     * @date 2019/7/11/011
     */
    private static boolean existLeaveType(List<TemplateComponent> componentsContent) {

        boolean result = false;
        for (TemplateComponent component : componentsContent) {
            String value = component.getValue();
            if (StringUtils.isNotEmpty(value) && "年假".equals(value)) {
                result = true;
                break;
            }
        }
        return result;
    }


	/*private static boolean isAnnualLeave(List<TemplateComponent> componentsContent) {
            boolean result=false;
			for (TemplateComponent component : componentsContent) {
                String value = component.getValue();
                if (StringUtils.isNotEmpty(value) && "年假".equals(value)) {
                    result=true;
				}
			}
		return result;
	}*/

    private static void checkRequiredFields(List<TemplateComponent> templateComponents, List<TemplateComponent> componentContents) {
        for (TemplateComponent templateComponent : templateComponents) {
            // 补卡班次和补卡时段必填
            if (templateComponent.getType() == ContentType.RECEIVE_CLOCK_TICK
                    || templateComponent.getType() == ContentType.RECEIVE_CLOCK_TIME) {
                continue;
            }

            if (templateComponent.getNeed() == 1) {
                boolean required = false;
                for (TemplateComponent componentContent : componentContents) {
                    if (Objects.equals(templateComponent.getNum(), componentContent.getNum())) {
                        if (StringUtils.isNotEmpty(componentContent.getValue())) {
                            required = true;
                            break;
                        }
                    }
                }

                if (!required) {
                    throw new RpcServerLogicalException(1, templateComponent.getName() + "不能为空");
                }
            }
        }
    }


    public static void checkRequestContents(Request request, Template template, User user, SubmitRequestDao submitRequestDao,
                                            SelectTemplateDao selectTemplateDao, AppendSignDao appendSignDao) throws Throwable {
        List<TemplateComponent> templateComponents = JSONArray.parseArray(template.getTemplateComponent(), TemplateComponent.class);
        if (ListHelper.isNullEmpty(request.getComponentsContent())) {
            throw new RpcServerLogicalException(1, "模板组件为空");
        }

        logger.info("模板组件:{}", JSONObject.toJSONString(templateComponents));
        logger.info("提交组件值:{}", JSONObject.toJSONString(request.getComponentsContent()));

        specialLogicCheck(request, template);
        // 检查所有组件的必填项
        checkRequiredFields(templateComponents, request.getComponentsContent());

        // 组件集
        List<ComponentGroup> componentGroups = null;
        if (StringUtils.isNotEmpty(template.getComponentGroupJson())) {
            String groupIds = ApprovalUtils.getComponentGroupIds(template.getComponentGroupJson());
            componentGroups = selectTemplateDao.selectComponentGroupInfoList(user.getCompanyId(), groupIds);
            ApprovalUtils.setComponentGroupNum(template.getComponentGroupJson(), componentGroups);
        }

        switch (template.getDefaultType()) {
            case Template.EXPENSES:
                boolean isNewExpense = request.getTotalMoney() != null;
                checkExpenseTemplate(templateComponents, request.getComponentsContent(), isNewExpense, template.getDefaultType());
                break;

            case Template.LEAVE:
                checkLeaveTemplateByGeneration(request, templateComponents, user.getTimeZone(), template.getDefaultType());
                break;

            case Template.OUTDOOR:
                checkOutDoorTemplate(templateComponents, request.getComponentsContent(), template.getDefaultType());
                break;

            case Template.PURCHASE:
            case Template.OTHER:
                commonTemplateCheck(templateComponents, request.getComponentsContent(), componentGroups);
                break;
            case Template.APPEND_SIGN:
                checkCheckInTemplate(request, template, appendSignDao, user);
                break;

            case Template.BUSINESS_TRIP:
                checkBusinessTrip(templateComponents, request.getComponentsContent(), request.getOuterPosition(), template.getDefaultType());
                break;

            case Template.COMPENSATORY_LEAVE:
                checkCompensatoryLaveTemplate(request, template, submitRequestDao, user);
                break;

            case Template.WORK_OVERTIME:
                checkWorkOverTimeTemplate(templateComponents, request.getComponentsContent(), request.getGeneration(), template.getDefaultType(), user);
                break;

            case Template.DAYS_OFF:
                checkDaysOffTemplate(templateComponents, request.getComponentsContent());
                break;

            default:
                throw new RpcServerLogicalException(1, "不存在的审批类型");
        }
    }

    private static void checkDaysOffTemplate(List<TemplateComponent> templateComponents, List<TemplateComponent> componentContents) {
        SimpleDateFormat sdf = DateUtils.getSimpleDateFormat(DateUtils.YMDHM);
        long startTime = 0;
        long endTime = 0;

        for (TemplateComponent templateComponent : templateComponents) {
            for (TemplateComponent componentContent : componentContents) {
                if (!Objects.equals(templateComponent.getNum(), componentContent.getNum())) {
                    continue;
                }

                if (templateComponent.getNum() == 2 || templateComponent.getNum() == 3) {
                    if (!ValidateUtil.isValidDateTime(componentContent.getValue())) {
                        throw new RpcServerLogicalException(1, templateComponent.getName() + "格式不正确");
                    }

                    if (templateComponent.getNum() == 2) {
                        startTime = parseTime(componentContent.getValue(), sdf);
                    }

                    if (templateComponent.getNum() == 3) {
                        endTime = parseTime(componentContent.getValue(), sdf);
                    }

                }


                if (templateComponent.getNum() == 4) {
                    if (templateComponent.getType() != ContentType.DAYS_OFF_TOTAL_TIME) {
                        throw new RpcServerLogicalException(1, "时长类型错误");
                    }

                    try {
                        DaysOffTimeInfo daysOffTimeInfo = JSONObject.parseObject(componentContent.getValue(), DaysOffTimeInfo.class);
                        if (StringUtils.isEmpty(daysOffTimeInfo.getLeaveTimestamp())
                                || StringUtils.isEmpty(daysOffTimeInfo.getTotalTime())
                                || ListHelper.isNullEmpty(daysOffTimeInfo.getTimeInfos())) {
                            throw new RpcServerLogicalException(1, "调休时长参数格式错误");
                        }
                    } catch (Throwable e) {
                        logger.error(componentContent.getValue());
                        throw new RpcServerLogicalException(1, "调休时长参数格式错误");
                    }

                }
            }
        }

        checkLeaveTime(startTime, endTime);
    }

    private static void specialLogicCheck(Request request, Template template) {
        if (Objects.equals(template.getIsAllowedToEditJudger(), 1) || RequestFlowType.ALTERNATIVE.equals(request.getRequestFlow())) {
            if (StringUtils.isEmpty(request.getAllJudgerToEdit()) && Utils.isEmpty(request.getFlows())) {
                throw new RpcServerLogicalException(1, "请选择第一层审批人");
            }
        }
    }


    private static void commonTemplateCheck(List<TemplateComponent> templateComponents
            , List<TemplateComponent> componentContents, List<ComponentGroup> componentGroups) {
        for (TemplateComponent templateComponent : templateComponents) {
            for (TemplateComponent componentContent : componentContents) {
                if (!Objects.equals(templateComponent.getNum(), componentContent.getNum())) {
                    continue;
                }
                // 前端有bug,先过滤掉错误数据
                if (!Objects.equals(templateComponent.getType(), componentContent.getType())) {
                    logger.error("提交数据异常");
                    EmailUtils.sendErrorMail("提交数据异常", JSONObject.toJSONString(componentContents));
                    continue;
                }

                logger.info("componentContent:{}", JSONObject.toJSONString(componentContent));

                int num = templateComponent.getNum();
                int type = templateComponent.getType();
                int need = templateComponent.getNeed();
                String name = templateComponent.getName();
                String value = componentContent.getValue();

                logger.info("num:{}, type:{}, need:{}, name:{}, value:{}", num, type, need, name, value);

                ContentTypeCheck.checkContent(type, name, value, need, null);

                // 组件集
                if (type == ContentType.COMPONENT_GROUP) {
                    if (ListHelper.isNullEmpty(componentGroups)) {
                        throw new RpcServerLogicalException(1, "模板中不存在组件集");
                    }

                    ComponentGroup componentGroup = getGroup(componentGroups, num);
                    if (Utils.isNull(componentGroup)) {
                        throw new RpcServerLogicalException(1, "模板中不存在对应的组件集");
                    }

                    List<GroupContent> groupContents = componentContent.getGroupContents();
                    if (ListHelper.isNullEmpty(groupContents)) {
                        throw new RpcServerLogicalException(1, "组件集内容不能为空");
                    }

                    componentGroupCheck(componentGroup.getComponents(), groupContents);

                    componentContent.setGroupId(componentGroup.getId());
                }

                // 金额汇总,只校验内容格式
                if (type == ContentType.TOTAL_MONEY) {
                    ContentTypeCheck.checkContent(ContentType.MONEY, name, value, need, null);
                }
            }
        }
    }

    private static void componentGroupCheck(List<Component> components, List<GroupContent> groupContents) {
        for (Component component : components) {
            int need = component.getNeed();
            int type = component.getType();
            String name = component.getName();
            List<String> chosenList = component.getSingleChosen();
            int componentNum = component.getNum();

            for (GroupContent groupContent : groupContents) {
                if (Utils.isNull(groupContent.getSort())) {
                    throw new RpcServerLogicalException(1, "组件集序号不能为空");
                }

                for (Component valueComponent : groupContent.getValue()) {
                    if (Utils.isNull(valueComponent.getNum())) {
                        throw new RpcServerLogicalException(1, "组件集num不能为空");
                    }
                    if (valueComponent.getNum().equals(componentNum)) {
                        valueComponent.setType(component.getType());
                        valueComponent.setName(component.getName());
                        ContentTypeCheck.checkContent(type, name, valueComponent.getValue(), need, chosenList);
                    }
                }
            }
        }
    }


    private static ComponentGroup getGroup(List<ComponentGroup> componentGroups, int num) {
        for (ComponentGroup group : componentGroups) {
            if (group.getNum().equals(num)) {
                return group;
            }
        }
        return null;
    }


    private static void checkBusinessTrip(List<TemplateComponent> templateComponents, List<TemplateComponent> componentContents, String outPosition, int defaultType) {
        if (defaultType == Template.BUSINESS_TRIP) {
            SimpleDateFormat sdf = DateUtils.getSingleSDF(DateUtils.YMDHM);

            // 出差开始时间、返回时间
            Long startTime = null;
            Long endTime = null;
            int countSite = 0;
            for (TemplateComponent templateComponent : templateComponents) {
                for (TemplateComponent componentContent : componentContents) {
                    if (!Objects.equals(templateComponent.getNum(), componentContent.getNum())) {
                        continue;
                    }

                    int num = templateComponent.getNum();
                    int need = templateComponent.getNeed();
                    int type = templateComponent.getType();
                    String name = templateComponent.getName();
                    String value = componentContent.getValue();

                    if (need == 1 && StringUtils.isEmpty(value)) {
                        throw new RpcServerLogicalException(1, name + "不能为空");
                    }

                    if (type == ContentType.LOCATION && num == 2 && StringUtils.isNotEmpty(value)) {
                        countSite++;
                    }

                    if (type == ContentType.LOCATION && num == 3 && StringUtils.isNotEmpty(value)) {
                        try {
                            JSONArray jsonArray = JSONArray.parseArray(value);
                            countSite += jsonArray.size();
                        } catch (Throwable e) {
                            logger.error(e.getMessage(), e);
                            throw new RpcServerLogicalException(1, "目的地数据格式不正确");
                        }
                    }

                    if (type == ContentType.TIME) {
                        if (num == 6) {
                            startTime = parseTime(value, sdf);
                        }
                        if (num == 7) {
                            endTime = parseTime(value, sdf);
                        }
                    }
                }
            }

            checkBusinessTrip(startTime, endTime);
            checkBussinessTripPosition(outPosition, countSite);
        }
    }


    private static void checkBussinessTripPosition(String outPosition, int countSite) {
        logger.info("countSite:{}, outPosition:{}", countSite, outPosition);
        if (countSite == 0 && StringUtils.isNotEmpty(outPosition)) {
            throw new RpcServerLogicalException(1, "获取定位失败，请重试");
        }

        if (countSite > 0 && StringUtils.isEmpty(outPosition)) {
            throw new RpcServerLogicalException(1, "获取定位失败，请重试");
        }

        if (countSite != 0 && StringUtils.isNotEmpty(outPosition)) {
            // 地点数目 跟 经纬度的数目必须要相同
            String[] positionList = outPosition.split("#");
            if (positionList.length != countSite) {
                throw new RpcServerLogicalException(1, " 获取定位失败，请重试");
            }
        }
    }


    private static void checkBusinessTrip(Long startTime, Long endTime) {
        // 出差开始时间、返回时间
        if (Utils.isNull(startTime) || Utils.isNull(endTime)) {
            throw new RpcServerLogicalException(1, "请填写开始时间或返回时间");
        }

        if (startTime.compareTo(endTime) >= 0) {
            throw new RpcServerLogicalException(1, "出发时间不得晚于返回时间");
        }

        if (TimeUtils.compareWithCurrentTimeWithoutTime(startTime) == -1) {
            throw new RpcServerLogicalException(1, "出发日期不得早于当前日期");
        }


    }


}
